import type {
    UseModalReturnType,
    ModalMethods,
    ModalProps,
    ReturnMethods,
    UseModalInnerReturnType,
} from '../types'

import {
    ref,
    onUnmounted,
    unref,
    getCurrentInstance,
    reactive,
    watchEffect,
    nextTick,
    toRaw,
} from 'vue'
import { isProdMode } from '/@/utils/env'
import { isFunction } from '/@/utils/is'
import { isEqual } from 'lodash-es'
import { tryOnUnmounted, isInSetup } from '/@/utils/helper/vueHelper'
import { error } from '/@/utils/log'
import { computed } from 'vue'
const dataTransferRef = reactive<any>({})

const visibleData = reactive<{ [key: number]: boolean }>({})

/**
 * @description: Applicable to independent modal and call outside
 */
export function useModal(): UseModalReturnType {
    isInSetup()
    const modalRef = ref<Nullable<ModalMethods>>(null)
    const loadedRef = ref<Nullable<boolean>>(false)
    const uidRef = ref<string>('')

    function register(modalMethod: ModalMethods, uuid: string) {
        uidRef.value = uuid

        isProdMode() &&
            onUnmounted(() => {
                modalRef.value = null
                loadedRef.value = false
                dataTransferRef[unref(uidRef)] = null
            })
        if (unref(loadedRef) && isProdMode() && modalMethod === unref(modalRef)) return

        modalRef.value = modalMethod
        modalMethod.emitVisible = (visible: boolean, uid: number) => {
            visibleData[uid] = visible
        }
    }

    const getInstance = () => {
        const instance = unref(modalRef)
        if (!instance) {
            error('useModal instance is undefined!')
        }
        return instance
    }

    const methods: ReturnMethods = {
        setModalProps: (props: Partial<ModalProps>): void => {
            getInstance()?.setModalProps(props)
        },
        getVisible: computed((): boolean => {
            return visibleData[~~unref(uidRef)]
        }),

        openModal: <T = any>(visible = true, data?: T, openOnSet = true): void => {
            getInstance()?.setModalProps({
                visible: visible,
            })

            if (!data) return

            if (openOnSet) {
                dataTransferRef[unref(uidRef)] = null
                dataTransferRef[unref(uidRef)] = toRaw(data)
                return
            }
            const equal = isEqual(toRaw(dataTransferRef[unref(uidRef)]), toRaw(data))
            if (!equal) {
                dataTransferRef[unref(uidRef)] = toRaw(data)
            }
        },
    }
    return [register, methods]
}

export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
    const modalInstanceRef = ref<Nullable<ModalMethods>>(null)
    const currentInstance = getCurrentInstance()
    const uidRef = ref<string>('')

    // currentInstall.type.emits = [...currentInstall.type.emits, 'register'];
    // Object.assign(currentInstall.type.emits, ['register']);

    const getInstance = () => {
        const instance = unref(modalInstanceRef)
        if (!instance) {
            error('useModalInner instance is undefined!')
        }
        return instance
    }

    const register = (modalInstance: ModalMethods, uuid: string) => {
        isProdMode() &&
            tryOnUnmounted(() => {
                modalInstanceRef.value = null
            })
        uidRef.value = uuid
        modalInstanceRef.value = modalInstance
        currentInstance?.emit('register', modalInstance, uuid)
    }

    watchEffect(() => {
        const data = dataTransferRef[unref(uidRef)]
        if (!data) return
        if (!callbackFn || !isFunction(callbackFn)) return
        nextTick(() => {
            callbackFn(data)
        })
    })

    return [
        register,
        {
            changeLoading: (loading = true) => {
                getInstance()?.setModalProps({ loading })
            },
            getVisible: computed((): boolean => {
                return visibleData[~~unref(uidRef)]
            }),

            changeOkLoading: (loading = true) => {
                getInstance()?.setModalProps({ confirmLoading: loading })
            },

            closeModal: () => {
                getInstance()?.setModalProps({ visible: false })
            },

            setModalProps: (props: Partial<ModalProps>) => {
                getInstance()?.setModalProps(props)
            },
        },
    ]
}
